//
// Created by wxd on 2025/8/3.
//

/**
 * 帧率计数器
 * 功能点：1. 记录FPS峰值(最大最小) 2. 获取当前FPS 3. 提供FPS的预估值 4. FPS的帧更新 5. 帧的历史统计，例如历史N帧FPS数据，N帧统计量的计算
 *       6. GPU记时，结合OpenGL时间戳查询，计算GPU FPS
 *
 * 1. 基于C++11提供的chrono实现高精度记时间，这里如果使用GLAD中提供的glfwGetTime，其精度取决于系统，可能不够精确
 * 2. 基于EMA指数平滑计算FPS帧率，避免帧率震荡导致的误差
 * 3. 基于std::mutex保护共享数据，实现一个线程安全的帧率统计
 * 4. 使用宏定义实现帧率日志的条件输出，仅DEBUG模式下输出(因为输出是一个耗时操作，频繁打印可能会出现卡顿)
 * 5. 在计算FPS的时候，我们会提供FPS的预估值，通常情况下，我们计算的帧率是指1s内的帧更新次数，但是某些情况下，不足1s我们仍然需要统计处当前的帧率值
 *    例如：
 *      即时反馈需求：应用启动或场景切换时，需要立即显示性能指标，不能等待完整1秒
 *      低帧率情况：当帧率非常低时（如<1FPS），等待完整1秒会导致反馈延迟过长
 *      平滑过渡：避免FPS显示从0突然跳到实际值，提供更平滑的用户体验
 *
 * EMA指数平滑FPS帧率计算算法实现：
 *    EMA（Exponential Moving Average，指数移动平均）是一种常用的平滑计算方法，它给予近期数据更高的权重，而对历史数据权重按指数递减。
 *    EMA平滑公式：EMA_t = α × current_value + (1-α) × EMA_{t-1}，本质上就是一次线性插值，其中EMA_{t-1}是历史值，通过α控制权
 *    重
 *
 * 预估帧率的常用算法：
 *    1. 基于时间差的倒数法: 1 / (cur_time - last_time)
 *    2. 移动平均法: 记录历史N帧的间隔时间，计算其平均值，为了保持N帧窗口的大小，需要移除最近最久的一帧， 更新当前帧的间隔
 *    3. 加权平均法: 给近期帧更高的权重，这里也是基于当前N帧(N=2或者更多)
 */


#pragma once

#ifndef ENGINE_FRAMECOUNTER_H
#define ENGINE_FRAMECOUNTER_H

#include <chrono>
#include <vector>
#include <mutex>
#include <algorithm>
#include <iostream>
#include <optional>
#include <utility>
#include <limits>
#include <memory>
#include <GLFW/glfw3.h>

class FrameCounter {
public:
    // FPS预估算法类型
    enum class EstimateFpsType {
        TIME_DIFF,                  // 时间差倒数法
        SMOOTH_MOVE_AVERAGE,        // 平滑移动平均法
        WEIGHTED_AVERAGE,           // 加权平均法
    };

    // 基于移动语义的Builder模式设置FrameCounter的配置项
    class Builder {
    public:

        Builder&& setGpuTimeAvailable(bool available) && {
            m_GPUTimeAvailable = available;
            return std::move(*this);
        }

        Builder&& setEmaAlpha(float alpha) && {
            m_EMA_Alpha = alpha;
            return std::move(*this);
        }

        Builder&& setHistoryMaxSize(size_t maxSize) && {
            m_HistoryMaxSize = maxSize;
            return std::move(*this);
        }

        Builder&& setAlphaCoef(std::vector<double>& coef) && {
            m_AlphaCoef = coef;
            return std::move(*this);
        }

        Builder&& setEstimateFpsType(EstimateFpsType type) && {
            m_EstimateFpsType = type;
            return std::move(*this);
        }

        std::unique_ptr<FrameCounter> build() && {
            return std::make_unique<FrameCounter>(std::move(*this));
        }

    private:
        bool m_GPUTimeAvailable;
        double m_EMA_Alpha;
        size_t m_HistoryMaxSize;
        std::vector<double> m_AlphaCoef;
        EstimateFpsType m_EstimateFpsType;

        friend class FrameCounter;
    };

    void updateFrame();                                 // 帧更新
    void updateGpuFPS();                                // GPU帧更新（带GPU的计时，需要OpenGL的支持）
    double getRealFPS() const;                          // 获取实际FPS
    double getSmoothFPS() const;                        // 获取平滑后的FPS
    double getRealMinFPS() const;                       // 获取实际历史帧数的最小帧率
    double getRealMaxFPS() const;                       // 获取实际历史帧数的最大帧率
    double getSmoothMinFPS() const;                     // 获取平滑历史帧数的最小帧率
    double getSmoothMaxFPS() const;                     // 获取平滑历史帧数的最大帧率
    double getRealAverageFPS(bool isWeighted) const;    // 获取实际FPS帧率的平均值
    double getSmoothAverageFPS() const;                 // 获取平滑FPS帧率的平均值
    std::optional<double> getGpuFPS() const;            // 获取带GPU的FPS帧率 (如果支持)

    explicit FrameCounter(Builder&& builder) : m_GPUTimeAvailable(std::move(builder.m_GPUTimeAvailable)),
                                               m_EMA_Alpha(std::move(builder.m_EMA_Alpha)),
                                               m_HistoryMaxSize(std::move(builder.m_HistoryMaxSize)),
                                               m_AlphaCoef(std::move(builder.m_AlphaCoef)),
                                               m_EstimateFpsType(std::move(builder.m_EstimateFpsType)) {
        m_LastFrameTimeStartPoint  = TimePoint{};
        m_LastFrameTimePoint = TimePoint{};
        m_CurrentFrameTimePoint = TimePoint{};
        m_CurrentDeltaFrameTime = Duration::zero();

        m_FrameCount = 0;
        m_CurrentRealFPS = 0;
        m_CurrentSmoothFPS = 0;
        m_MinRealFPS = std::numeric_limits<double>::max();
        m_MaxRealFPS = std::numeric_limits<double>::min();
        m_MinSmoothFPS = std::numeric_limits<double>::max();
        m_MaxSmoothFPS = std::numeric_limits<double>::min();
        m_SmoothFpsHistory = {};
        m_RealFpsHistory = {};
        m_CurrentGpuFPS = 0;
        m_IsFirstFrame = true;

        if (m_HistoryMaxSize <= 0) {
            m_HistoryMaxSize = DEFAULT_FRAME_CACHE_WINDOW_SIZE; // 默认统计30帧历史记录，后续扩展，根据不同的场景自适应调整N帧记录
        }

        // 校验数据
        if (!checkAlphaCoef()) {

        }
    }

private:
    using Clock = std::chrono::high_resolution_clock;   // 用于精确时间测量对象
    using TimePoint = std::chrono::time_point<Clock>;   // 用于记录精确时间点对象
    using Duration = std::chrono::duration<double>;     // 用于记录精确时间间隔对象

    mutable std::mutex m_FpsMutex;                       // 线程锁，保证统计的线程安全

    TimePoint m_LastFrameTimeStartPoint;                 // 计算帧率起始时间
    TimePoint m_LastFrameTimePoint;                      // 上一次更新时间
    TimePoint m_CurrentFrameTimePoint;                   // 当前帧更新时间
    Duration m_CurrentDeltaFrameTime;                    // 当前时间间隔

    int m_FrameCount;                                    // 当前帧计数
    double m_CurrentRealFPS;                             // 实际FPS
    double m_CurrentSmoothFPS;                           // 平滑之后的FPS
    double m_MinRealFPS;                                 // 历史最小帧率
    double m_MaxRealFPS;                                 // 历史最大帧率
    double m_MinSmoothFPS;                               // 历史最小帧率
    double m_MaxSmoothFPS;                               // 历史最大帧率

    // 基于一个简单的环形缓冲记录
    std::vector<double> m_SmoothFpsHistory;              // 平滑FPS历史记录缓存
    std::vector<double> m_RealFpsHistory;                // 实际FPS历史记录缓存
    std::vector<double> m_IntervalFrameHistory;          // 历史帧间隔时间

    double m_CurrentGpuFPS;                              // GPU FPS
    bool m_IsFirstFrame;                                 // 是否为第一帧


    // 可配置参数
    bool m_GPUTimeAvailable;                             // 是否支持GPU计时
    double m_EMA_Alpha;                                  // EMA指数平滑插值系数，用于FPS帧率的平滑计算
    size_t m_HistoryMaxSize;                             // 历史记录的最大宽度，控制当前历史帧的窗口大小
    std::vector<double> m_AlphaCoef;                     // N帧插值系数
    EstimateFpsType m_EstimateFpsType;                   // 记录预估FPS算法类型

    const size_t DEFAULT_FRAME_CACHE_WINDOW_SIZE = 30;

    // 校验设置的插值系数是否满足和为1，且每个系数 >= 0
    bool checkAlphaCoef() const {
        if (m_AlphaCoef.empty()) {
            return false;
        }

        double sum = 0;

        for (const auto& coef : m_AlphaCoef) {
            if (coef < 0 || coef > 1) {
                return false;
            }
            sum += coef;
        }

        return sum == 1;
    }
};
#endif //FRAMECOUNTER_H